ποΈGitΠ―ΡΠ°ποΈ
specs/006-kmp-project-structure/quickstart.md bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 5.94 KB
Tc9d1d9# Quickstart: KMP Recommended Project Structure Migration
Tc9d1d9## Prerequisites
Tff7b72- JDK 21 installed
Tff7b72- Ta5d6ff`ANDROID_HOME` set
Tff7b72- Proto submodule initialized: Ta5d6ff`git submodule update --init`
Tff7b72- Ta5d6ff`local.properties` exists: Ta5d6ff`[ -f local.properties ] || cp secrets.defaults.properties local.properties`
Tff7b72- Clean build baseline: Ta5d6ff`./gradlew assembleDebug` passes before starting
Tc9d1d9## Migration Steps
Tc9d1d9### Step 1: Harden convention plugin defaults
**File**: Ta5d6ff`build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt`
In Ta5d6ff`configureKotlinMultiplatform()`, add Ta5d6ff`androidResources.enable = false` as default inside the Ta5d6ff`pluginManager.withPlugin` block:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3pluginManagerTb4b4b4.Te6edf3withPluginTb4b4b4(Ta5d6ff"Ta5d6ffcom.android.kotlin.multiplatform.libraryTa5d6ff"Tb4b4b4) Tb4b4b4{
Te6edf3extensionsTb4b4b4.Te6edf3findByTypeTff7b72<Te6edf3KotlinMultiplatformAndroidLibraryTargetTff7b72>Tb4b4b4(Tb4b4b4)Tff7b72?.Te6edf3apply Tb4b4b4{
Te6edf3compileSdk Tff7b72= Te6edf3configPropertiesTb4b4b4.Te6edf3getPropertyTb4b4b4(Ta5d6ff"Ta5d6ffCOMPILE_SDKTa5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Te6edf3minSdk Tff7b72= Te6edf3configPropertiesTb4b4b4.Te6edf3getPropertyTb4b4b4(Ta5d6ff"Ta5d6ffMIN_SDKTa5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Te6edf3androidResourcesTb4b4b4.Te6edf3enable Tff7b72= Tff7b72false T8b949e// β NEW: Default for all KMP modules
Tff7b72if Tb4b4b4(Te6edf3namespace Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3pkg Tff7b72= Tff7b72thisTf0883e@configureKotlinMultiplatform.path.removePrefixTb4b4b4(Ta5d6ff"Ta5d6ff:Ta5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3replaceTb4b4b4(Ta5d6ff"Ta5d6ff:Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff.Ta5d6ff"Tb4b4b4)
Te6edf3namespace Tff7b72= Ta5d6ff"Ta5d6fforg.meshtastic.Tffd700$Te6edf3pkgTa5d6ff"
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Ta5d6ff```
**Verify**: Ta5d6ff`./gradlew assembleDebug` β should pass (no behavior change yet, modules still override).
Tc9d1d9### Step 2: Migrate Tier 1 modules (minimal β 6 modules)
For each module, change Ta5d6ff`android {}` to Ta5d6ff`androidLibrary {}` inside Ta5d6ff`kotlin {}`, removing properties now handled by convention.
**Pattern β before**:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3kotlin Tb4b4b4{
Te6edf3jvmTb4b4b4(Tb4b4b4) T8b949e// β remove if present (convention handles it)
Te6edf3android Tb4b4b4{
Te6edf3namespace Tff7b72= Ta5d6ff"Ta5d6fforg.meshtastic.core.diTa5d6ff" T8b949e// β auto-derived, remove
Te6edf3androidResourcesTb4b4b4.Te6edf3enable Tff7b72= Tff7b72false T8b949e// β convention default, remove
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Ta5d6ff```
**Pattern β after**:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3kotlin Tb4b4b4{
T8b949e// android target configured by convention plugin (namespace auto-derived, resources disabled)
Te6edf3sourceSets Tb4b4b4{ T8b949e/* ... */ Tb4b4b4}
Tb4b4b4}
Ta5d6ff```
**Modules**: Ta5d6ff`core:di`, Ta5d6ff`core:nfc`, Ta5d6ff`core:ui`, Ta5d6ff`core:navigation`, Ta5d6ff`feature:messaging`, Ta5d6ff`feature:settings`
**Verify after each module**: Ta5d6ff`./gradlew :<module>:assembleDebug :<module>:allTests`
**Verify after batch**: Ta5d6ff`./gradlew assembleDebug allTests`
Tc9d1d9### Step 3: Migrate Tier 2 modules (withHostTest β 18 modules)
**Pattern β before**:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3kotlin Tb4b4b4{
Te6edf3jvmTb4b4b4(Tb4b4b4)
Te6edf3android Tb4b4b4{
Te6edf3namespace Tff7b72= Ta5d6ff"Ta5d6fforg.meshtastic.core.dataTa5d6ff"
Te6edf3androidResourcesTb4b4b4.Te6edf3enable Tff7b72= Tff7b72false
Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Ta5d6ff```
**Pattern β after**:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Ta5d6ff```
**Special case β `feature:wifi-provision`** (namespace mismatch):
Ta5d6ff```Ta5d6ffkotlin
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3namespace Tff7b72= Ta5d6ff"Ta5d6fforg.meshtastic.feature.wifiprovisionTa5d6ff"
Te6edf3withHostTest Tb4b4b4{Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Ta5d6ff```
**Verify after batch**: Ta5d6ff`./gradlew assembleDebug :desktop:packageUberJarForCurrentOS allTests`
Tc9d1d9### Step 4: Migrate Tier 3 modules (special β 3 modules)
Tc9d1d9#### `core:proto`:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3minSdk Tff7b72= T79c0ff2T79c0ff1 T8b949e// ATAK compatibility override
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Ta5d6ff```
Tc9d1d9#### `core:database`:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4}
Te6edf3withDeviceTest Tb4b4b4{ Te6edf3instrumentationRunner Tff7b72= Ta5d6ff"Ta5d6ffandroidx.test.runner.AndroidJUnitRunnerTa5d6ff" Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Ta5d6ff```
Tc9d1d9#### `core:resources`:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3kotlin Tb4b4b4{
Te6edf3androidLibrary Tb4b4b4{
Te6edf3androidResources Tb4b4b4{
Te6edf3enable Tff7b72= Tff7b72true T8b949e// Override convention default
Te6edf3resourcePrefix Tff7b72= Ta5d6ff"Ta5d6ffmeshtastic_Ta5d6ff"
Tb4b4b4}
Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4}
Tb4b4b4}
T8b949e// ...
Tb4b4b4}
Ta5d6ff```
**Verify**: Ta5d6ff`./gradlew assembleDebug :desktop:packageUberJarForCurrentOS allTests`
Tc9d1d9### Step 5: Full verification
Ta5d6ff```Ta5d6ffbash
T8b949e# Full Android build
./gradlew assembleDebug
T8b949e# Full Desktop build
./gradlew :desktop:packageUberJarForCurrentOS
T8b949e# All tests
./gradlew allTests
T8b949e# Desktop-only mode (no Android SDK)
Te6edf3DESKTOP_ONLYTff7b72=Tffa657true ./gradlew :desktop:packageUberJarForCurrentOS
T8b949e# Lint and formatting
./gradlew spotlessApply spotlessCheck detekt
T8b949e# Verify no legacy android {} blocks remain in KMP modules
grep -rn Ta5d6ff"android {" core/*/build.gradle.kts feature/*/build.gradle.kts Tb4b4b4| grep -v Ta5d6ff"widget\|api\|barcode\|androidLibrary\|androidResources\|androidMain\|androidHostTest\|androidDeviceTest\|androidRuntimeClasspath"
T8b949e# Expected: zero matches
Ta5d6ff```
Tc9d1d9### Step 6: Validate success criteria
Tff7b72- Tff7b72[ ] SC-001: Zero Ta5d6ff`android {}` blocks inside Ta5d6ff`kotlin {}` in KMP modules
Tff7b72- Tff7b72[ ] SC-002: Ta5d6ff`assembleDebug` and Ta5d6ff`:desktop:packageUberJarForCurrentOS` pass
Tff7b72- Tff7b72[ ] SC-003: Ta5d6ff`allTests` passes with zero regressions
Tff7b72- Tff7b72[ ] SC-004: Ta5d6ff`DESKTOP_ONLY` build succeeds
Tff7b72- Tff7b72[ ] SC-005: Clean Ta5d6ff`assembleDebug` time within 5% of baseline
Tff7b72- Tff7b72[ ] SC-006: New module can apply Ta5d6ff`meshtastic.kmp.library` with no manual Ta5d6ff`android {}` block
Tff7b72- Tff7b72[ ] SC-007: Ta5d6ff`configureKotlinMultiplatform()` uses only Ta5d6ff`KotlinMultiplatformAndroidLibraryTarget` API
Tc9d1d9## Rollback Plan
If any step produces build failures that cannot be resolved:
**Per-module**: Ta5d6ff`git checkout -- <module>/build.gradle.kts` to revert a single module migration.
**Per-tier**:
Tff7b72- Tier 1: Ta5d6ff`git checkout -- core/di/build.gradle.kts core/nfc/build.gradle.kts core/ui/build.gradle.kts core/navigation/build.gradle.kts feature/settings/build.gradle.kts feature/messaging/build.gradle.kts`
Tff7b72- Tier 2: Ta5d6ff`git checkout -- core/{ble,common,data,domain,model,network,service,takserver,datastore,prefs,repository,testing}/build.gradle.kts feature/{connections,firmware,intro,map,node,wifi-provision}/build.gradle.kts`
Tff7b72- Tier 3: Ta5d6ff`git checkout -- core/{proto,database,resources}/build.gradle.kts`
**Full rollback**: Ta5d6ff`git stash` or Ta5d6ff`git checkout -- .` to revert all changes.
Convention plugin change (Step 1) is backward-compatible β existing explicit Ta5d6ff`androidResources.enable = false` in modules is a no-op override.
Served by rngit 1.5.4 - Generated in 0.03s